module Ch04(safeHead, safeTail, safeLast, safeInit, splitWith,
            firstWords, transposeLines, equalizeLengths, asInt,
            asInt_either, myConcat, takeWhileRecurse, takeWhileFold,
            myGroupByRecurse, myGroupByFold, myAny, myAny2, myAny3,
            myAny4, myAny5, myAny6, myCycle, myCycle2, myWords,
            myUnlines, myUnlines2) where

import Data.Char
import Data.List

safeHead [] = Nothing
safeHead (x:_) = Just x

safeTail [] = Nothing
safeTail (_:xs) = Just xs

safeLast [] = Nothing
safeLast [x] = Just x
safeLast (x:xs) = safeLast xs

safeInit [] = Nothing
safeInit [x] = Just []
safeInit (x:xs) = Just (x:rest) where
    Just rest = safeInit xs

splitWith _ [] = []
splitWith pred (x:xs)
    | pred x = splitWith pred xs
    | otherwise = ((x:acc):rest) where
    (acc,rest) = splitWithAccumulator pred xs

splitWithAccumulator _ [] = ([],[])
splitWithAccumulator pred (x:xs)
    | pred x = ([], splitWith pred xs)
    | otherwise = ((x:acc),rest) where
    (acc,rest) = splitWithAccumulator pred xs

firstWords = unlines . (map safeFirstWord) . lines where
    safeFirstWord = (maybe "" id) . safeHead . words

transposeLines = unlines . transpose . equalizeLengths . lines where
    transpose [] = []
    transpose ([]:_) = []
    transpose list = (map head list) : (transpose (map tail list))

equalizeLengths lines = map (padToLength ' ' (maxLength lines)) lines where
    maxLength [] = 0
    maxLength lines = (maximum (map length lines))
    padToLength padding 0 line = []
    padToLength padding n [] = padding : (padToLength padding (n-1) [])
    padToLength padding n (x:xs) = x : (padToLength padding (n-1) xs)

asInt_pos = foldl' (\x y -> 10 * x + (digitToInt y)) 0

asInt ('-' : num) = -(asInt_pos num)
asInt num = asInt_pos num

asInt_either_pos = foldl' helper (Right 0) where
    helper (Right acc) x
        | isDigit x = Right (10 * acc + (digitToInt x))
        | otherwise = Left ("non-digit '" ++ (x : "'"))
    helper (Left err) x = Left err

asInt_either ('-' : num) = negateRight (asInt_either_pos num) where
    negateRight (Right answer) = Right (-answer)
    negateRight (Left err) = Left err
asInt_either num = asInt_either_pos num

myConcat = foldr (++) []

takeWhileRecurse pred [] = []
takeWhileRecurse pred (x:xs)
    | pred x = (x : (takeWhileRecurse pred xs))
    | otherwise = []

takeWhileFold pred = foldr step [] where
    step x acc
        | pred x = x : acc
        | otherwise = []

myGroupByRecurse compare [] = []
myGroupByRecurse compare [x] = [[x]]
myGroupByRecurse compare (x:xs)
    | compare x (head xs) = ((x:first) : rest)
    | otherwise = [x] : (myGroupByRecurse compare xs)
    where (first:rest) = myGroupByRecurse compare xs

myGroupByFold compare = foldr step [] where
    step x [] = [[x]]
    step x (xs:rest)
         | compare x (head xs) = (x:xs) : rest
         | otherwise = [x] : xs : rest

myAny pred = foldr step False where
    step x _ | pred x = True
    step _ y = y

myAny2 pred = foldr step False where
    step _ True = True
    step x _ = pred x

myAny3 pred = foldr step False where
    step x y = (pred x) || y

myAny4 pred = foldr ((||) . pred) False

myAny5 pred = foldr step False where
    step x y = y || (pred x)
    
myAny6 pred = foldl' step False where
    step x y = (pred y) || x

myCycle list = foldr (:) (myCycle list) list

myCycle2 [] = error "myCycle2: empty list"
myCycle2 list = myCycle list

myWords = removeInitialEmptyList . (foldr step [[]]) where
    step char ([] : acc)
        | isSpace char = [] : acc
        | otherwise = [char] : acc
    step char acc
        | isSpace char = [] : acc
        | otherwise = (char : (head acc)) : (tail acc)
    removeInitialEmptyList ([]:rest) = rest
    removeInitialEmptyList rest = rest

myUnlines = foldr step "" where
    step line acc = line ++ "\n" ++ acc

myUnlines2 = foldr ((++) . (++ "\n")) ""
